package com.h.chatroom.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.h.chatroom.common.component.OnlineUserManager;
import com.h.chatroom.mapper.MessageMapper;
import com.h.chatroom.mapper.SessionMapper;
import com.h.chatroom.pojo.dataobject.Friend;
import com.h.chatroom.pojo.dataobject.Message;
import com.h.chatroom.pojo.request.MessageRequest;
import com.h.chatroom.pojo.response.MessageResponse;
import com.h.chatroom.service.WebSocketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.List;

@Service
@Slf4j
public class WebSocketServiceImpl implements WebSocketService {
    @Autowired
    private OnlineUserManager onlineUserManager;

    private ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private SessionMapper sessionMapper;

    @Autowired
    private MessageMapper messageMapper;

    @Override
    public void registerSession(WebSocketSession session) {
        // 在建立连接的时候 对其 进行 userId 和 WebSocketSession 进行 键值对的 连接
        Integer userId = (Integer) session.getAttributes().get("userId");
        if(userId == null) {
            return;
        }
        onlineUserManager.online(userId,session);
    }

    @Override
    public void processMessage(WebSocketSession session, TextMessage message) throws IOException {
        // 1. 获取发送者的Id和Name
        Integer userId = (Integer) session.getAttributes().get("userId");
        String username = (String) session.getAttributes().get("username");
        if(userId == null || username == null) {
            log.error("用户未登录，无法进行消息转发!");
            return;
        }
        // 2.message中信息是 JSON 格式的，需要将其转化为 Java字符串，转换为 MessageRequest 这个请求的类
        MessageRequest request = objectMapper.readValue(message.getPayload(),MessageRequest.class);
        // 先进行比较类型，如果是 message 就进行转发
        if (request.getType().equals("message")) {
            transferMessage(userId,username,request);
        }else {
            log.error("[WebSocket] type有误: " + request.getType());
        }
    }

    // 通过这个方法进行信息的转发
    private void transferMessage(Integer userId, String username, MessageRequest request) throws IOException {
        // 1.创建返回的类，表示返回的信息
        MessageResponse response = new MessageResponse();
        response.setType(request.getType());
        response.setFromId(userId);
        response.setFromName(username);
        response.setSessionId(request.getSessionId());
        response.setContent(request.getContent());
        // 时间的获取 与 转换
        Timestamp postTime = Timestamp.from(Instant.now());
        response.setPostTime(postTime);
        // 2.因为需要返回的 json字符串，所以这里需要 将 Java对象进行 序列化 转换为 json字符串
        String responseJson = objectMapper.writeValueAsString(response);

        // 3.需要知道吧这个信息发送给谁，那么需要根据 sessionId 和 userId 进行找到都需要发送给谁
        //   这里需要注意的是，同时也需要将信息发送给 发送者本身
        List<Friend> friends = sessionMapper.selectFriendBySessionId(request.getSessionId(), userId);
        //   将本身也加入到 friends 中进行发送信息
        Friend friendMy = new Friend();
        friendMy.setFriendId(userId);
        friendMy.setFriendName(username);
        friends.add(friendMy);

        // 4.需要把信息发送给所有的 好友在这个 sessionId中的，但是也是需要注意这个对应的好友是否在线，如果没有在线，那么就不将信息进行发送
        for(Friend friend : friends) {
            // 需要根据 获取到的 friend 中的 friendId 进行寻找到对应的 键值对中的 WebSocketSession 从而进行发送信息
            WebSocketSession session = onlineUserManager.getSession(friend.getFriendId());
            if(session == null) {
                // 什么也不做，到后面将信息放到 数据库中 当其上线之后，就可查看到历史信息
                continue;
            }
            // 这里注意，发送的是 这个类 TextMessage，所以需要把返回类的字符串放到 TextMessage 进行加工
            session.sendMessage(new TextMessage(responseJson));
        }
        // 5.转发的信息，需要放到数据库中，为了后续进行获取历史信息的时候呢，可以查询到结果
        //   并且对于 当用户下线的时候呢，没有进行发送信息，那么重新上线的时候，需要获取历史信息
        Message message = new Message();
        message.setFromId(userId);
        message.setSessionId(response.getSessionId());
        message.setContent(response.getContent());
        message.setPostTime(postTime);
        messageMapper.insertByMessage(message);
    }

    @Override
    public void handlerError(WebSocketSession session, Throwable exception) {
        // 发生错误，将其userId 对应的 WebSocketSession 从键值对中进行移除
        log.error("错误信息为: " + exception);
        Integer userId = (Integer) session.getAttributes().get("userId");
        if(userId == null) {
            return;
        }
        onlineUserManager.offline(userId,session);
    }

    @Override
    public void removeSession(WebSocketSession session, CloseStatus status) {
        // 正常退出
        Integer userId = (Integer) session.getAttributes().get("userId");
        if(userId == null) {
            return;
        }
        onlineUserManager.offline(userId,session);
    }
}
